home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 October / Macworld (1998-10).dmg / Shareware World / Info / For Developers / MacZoop 1.8.4 / More Classes / File Classes / ZGIFFile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-13  |  15.3 KB  |  440 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZGIFFile.cpp        -- a file object that can open GIF images
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22.  
  23. #include    "ZGIFFile.h"
  24. #include    "ZGWorld.h"
  25. #include    "MacZoop.h"
  26.  
  27.  
  28. static OSErr     ParseGIF( short refNum, GWorldPtr *world );
  29.  
  30. /*---------------------------------*** CONSTRUCTOR ***----------------------------------*/
  31.  
  32.  
  33. ZGIFFile::ZGIFFile( const FSSpec& aSpec )
  34.     : ZFile( aSpec )
  35. {
  36. }
  37.  
  38. /*------------------------------------*** READ ***--------------------------------------*/
  39. /*
  40. read the GIF file into a ZGWorld object
  41. ----------------------------------------------------------------------------------------*/
  42.  
  43. void    ZGIFFile::Read( ZGWorld*   aGWorld )
  44. {
  45.     GWorldPtr    gw;
  46.     
  47.     FailNILParam( aGWorld );
  48.     if ( refNum == _NOT_OPEN )
  49.         FailOSErr( fnOpnErr );
  50.     
  51.     gw = aGWorld->GetMacGWorld();
  52.     
  53.     if ( gw )
  54.         DisposeGWorld( gw );
  55.         
  56.     aGWorld->SetMacGWorld( gw = NULL );
  57.     
  58.     Read( &gw );
  59.     
  60.     aGWorld->SetMacGWorld( gw );
  61. }
  62.  
  63.  
  64. /*------------------------------------*** READ ***--------------------------------------*/
  65. /*
  66. read the GIF file into a Mac GWorld
  67. ----------------------------------------------------------------------------------------*/
  68.  
  69. void    ZGIFFile::Read( GWorldPtr* aGWorld )
  70. {
  71.     FailNILParam( aGWorld );
  72.     
  73.     if ( refNum == _NOT_OPEN )
  74.         FailOSErr( fnOpnErr );
  75.     
  76.     FailOSErr( ParseGIF( refNum, aGWorld ));    
  77. }
  78.  
  79.  
  80.  
  81. #pragma mark -
  82. #pragma mark #### GIF Parsing Stuff ####
  83.  
  84. /*
  85.  
  86. This code was supplied by James Bunk, though may not have been written by him. Modifications
  87. to the code to use exceptions for errors, etc. by Graham Cox.
  88.  
  89. */
  90.  
  91. #define ColorMapMask            0x80
  92. #define    ImageSeparator            0x2C
  93. #define    ExtensionIntroducer        0x21
  94. #define    NullBlock                0x00
  95. #define    InterlaceMask            0x40
  96. #define    BufferSize                8192                                    // May be freely modified
  97.  
  98. #define    abort(x)                { FailOSErr( x ); }
  99. #define    Check(x)                if (e) abort(x)                            // this means return e
  100. #define    Read(c, buf)            count = c;                                                                    \
  101.                                 e = FSRead (refNum, &count, buf);        /* Read the requested number of bytes */    \
  102.                                 Check(strCantRead)                                                            \
  103.                                 FileSize -= count                        /* And note that we have read them */
  104.                         
  105. #define    NextByte(b)                Read(1, &b)
  106.  
  107. #define    Skip(n)                    e = SetFPos (refNum, fsFromMark, n);    /* Skip n bytes */                        \
  108.                                 Check(strCantSetFPos)                                                        \
  109.                                 FileSize -= n                            // And note that we have read them */
  110.  
  111. #define    FillBuffer                {                                                                        \
  112.     BufferPointer = BufferBase;                                                                            \
  113.     Read (BufferSize <= FileSize ? BufferSize : FileSize, BufferBase);                                                \
  114. }
  115.  
  116. #define    BufferGetOne            {                                                                        \
  117.     if (++BufferPointer >= BufferTop)                                /* If we are at the end of the buffer */        \
  118.         FillBuffer                                                    /* Then fill it again */                    \
  119.     if (BlockCount == 0) {                                            /* If the next byte is a length byte */        \
  120.         BlockCount = *(BufferPointer++);                            /* Then skip it and update BlockCount */        \
  121.         if (BufferPointer == BufferTop)                                /* If this brings us to the end of the buffer */    \
  122.             FillBuffer                                                /* Then fill it again */                    \
  123.     }                                                                                                \
  124.     BlockCount--;                                                                                        \
  125. }
  126.  
  127. #define    ReadCode(code)            {                                                                        \
  128.     if (BitOffset == 0) BufferGetOne                                                                        \
  129.     data = *BufferPointer;                                            /* Read the current byte */                \
  130.     newBitOffset = BitOffset + CodeSize;                                                                    \
  131.     if (newBitOffset > 8) {                                            /* If we need more, read the next byte */    \
  132.         BufferGetOne                                                                                    \
  133.         data += ((long) (*BufferPointer)) << 8;                                                                \
  134.     }                                                                                                \
  135.     if (newBitOffset > 16) {                                        /* If we still need more, read a third byte */    \
  136.         BufferGetOne                                                                                    \
  137.         data += ((long) (*BufferPointer)) << 16;                                                                \
  138.     }                                                                                                \
  139.     data >>= BitOffset;                                                /* Skip the bits we already processed */        \
  140.     BitOffset = newBitOffset & 7;                                    /* Compute the new bit offset */            \
  141.     code = data & ReadMask;                                            /* Mask off the bits we don't want yet */    \
  142. }
  143.  
  144. #define    DrawPixel(index)        {                                                                    \
  145.     * (Byte*) curAddr = index;                                        /* Write out the pixel */                    \
  146.     curAddr = (long *) ((Byte*) curAddr + 1);                        /* Update the pointer */                    \
  147.     if (++xc == Width) {                                            /* Update the x-coordinate */                \
  148.         xc = 0;                                                        /* If it overflows, update the y-coordinate */    \
  149.         if (!Interlaced)                                            /* In a non-interlaced picture, just */        \
  150.             yc++;                                                    /* increment yc to the next scan line */        \
  151.         else {                                                                                        \
  152.             switch (Pass) {                                            /* Otherwise deal with the interlace as */    \
  153.             case 0:                                                    /* described in the GIF spec */                \
  154.                 yc += 8;                                                                                \
  155.                 if (yc >= Height) {    Pass++;    yc = 4;    }                                                    \
  156.                 break;                                                                                \
  157.             case 1:                                                                                    \
  158.                 yc += 8;                                                                                \
  159.                 if (yc >= Height) {    Pass++;    yc = 2;    }                                                    \
  160.                 break;                                                                                \
  161.             case 2:                                                                                    \
  162.                 yc += 4;                                                                                \
  163.                 if (yc >= Height) {    Pass++;    yc = 1;    }                                                    \
  164.                 break;                                                                                \
  165.             case 3:                                                                                    \
  166.                 yc += 2;                                                                                \
  167.                 break;                                                                                \
  168.             default:                                                                                    \
  169.                 break;                                                                                \
  170.             }                                                                                        \
  171.         }                                                                                            \
  172.         curAddr = (long*) ((long) srcBaseAddr + (long) yc * (long) rowBytes);    /* update current address in bitmap */        \
  173.     }                                                                                                \
  174. }
  175.  
  176. #define    ReadColorMap            {                                                                    \
  177.     Boolean        mapPresent;                                                                            \
  178.     CTabPtr        tablePtr;                                                                                \
  179.     short        i;                                                                                    \
  180.                                                                                                     \
  181.     mapPresent = (flags & ColorMapMask) ? true : false;                    /* Does the file have a global color map ? */    \
  182.     if (mapPresent) {                                                                                    \
  183.         BitsPerPixel = (flags & 7) + 1;                                    /* Image bit depth */                    \
  184.         ColorMapSize = 1 << BitsPerPixel;                                /* Number of entries in color table */        \
  185.         BitMask = ColorMapSize - 1;                                                                        \
  186.                                                                                                     \
  187.         if (ColorTable != NULL)                                            /* The local table overrides the global one */    \
  188.             DisposeHandle ((Handle) ColorTable);                        /* so let's get rid of the latter if it exists */    \
  189.         ColorTable = (CTabHandle) NewHandle (8*ColorMapSize+8);            /* Allocate memory for the table */        \
  190.         if (ColorTable == NULL) { e = memFullErr; abort(strGIFNoMem) }                                            \
  191.         HLock((Handle) ColorTable);                                                                        \
  192.         tablePtr = *ColorTable;                                                                            \
  193.         tablePtr->ctSeed = 0;                                            /* I don't know what to put in there */        \
  194.         tablePtr->ctFlags = 0;                                            /* so I just zero these fields */            \
  195.         tablePtr->ctSize = ColorMapSize - 1;                                                                \
  196.                                                                                                     \
  197.         for (i = 0; i < ColorMapSize; i++) {                                                                    \
  198.             tablePtr->ctTable[i].value = i;                                                                    \
  199.             NextByte(b);                                                                                \
  200.             tablePtr->ctTable[i].rgb.red = (short) b * 0x100;            /* Determine RGB value */                \
  201.             NextByte(b);                                                                                \
  202.             tablePtr->ctTable[i].rgb.green = (short) b * 0x100;                                                    \
  203.             NextByte(b);                                                                                \
  204.             tablePtr->ctTable[i].rgb.blue = (short) b * 0x100;                                                    \
  205.         }                                                                                            \
  206.         HUnlock((Handle) ColorTable);                                                                        \
  207.     }                                                                                                \
  208.     HasColorMap = HasColorMap || mapPresent;                                                                \
  209. }
  210.  
  211.  
  212. /************************************************************************
  213. ************************************************************************/
  214.  
  215. static OSErr     ParseGIF( short refNum,GWorldPtr *world)
  216. {
  217.     OSErr            e;                                                /* Error code */
  218.     long            sig;
  219.     long            count,FileSize;
  220.     Byte            b, flags, c;
  221.     short            BitsPerPixel, ColorMapSize, BitMask;
  222.     short            Width, Height;
  223.     short            ClearCode, FreeCode, EOFCode, FirstFree;
  224.     Byte            CodeSize, InitCodeSize;
  225.     short            MaxCode;
  226.     Byte            *BufferBase = NULL;                                /* Read buffer */
  227.     Byte            *BufferPointer, *BufferTop;                        /* Offset in buffer */
  228.     Byte            BlockCount;                                        /* Remaining bytes in current block */
  229.     Byte            BitOffset, newBitOffset;
  230.     long             data;
  231.     short            ReadMask;                                        /* Mask with exactly CodeSize bits set to 1 */
  232.     short            CurCode, InCode, OldCode, Code;                    /* Decompressor variables */
  233.     short            FinChar;
  234.     short            *OutCodeBase = NULL;                            /* Output array used by the decompressor */
  235.     short            *OutCode;                                        /* Current pointer into this array */
  236.     short            *Prefix = NULL;                                    /* The hash table used by the decompressor */
  237.     short            *Suffix = NULL;
  238.     short            xc, yc;                                            /* Pen position */
  239.     short            Pass;                                            /* Used by the output routines for interlaced pics */
  240.     Boolean            HasColorMap, Interlaced;
  241.     PixMapHandle    srcPixMap;                                        /* These are associated with 'world' */
  242.     long            *srcBaseAddr, *curAddr;                            /* Pixmap base address and current address */
  243.     short            rowBytes;
  244.     Rect            bounds;
  245.     CTabHandle        ColorTable = NULL;                                /* Custom color table */
  246.     GWorldPtr        oldWorld;                                        /* Some temporary variables */
  247.     GDHandle        oldDevice;
  248.     long            progRefNum = 0;                                    // progress bar ref number
  249.     GWorldFlags        gwflags;
  250.     
  251.     try
  252.     {
  253.         if (!(OutCodeBase = (short*) NewPtr(2050)))        { e = MemError(); abort(strGIFNoMem) }
  254.         if (!(Prefix = (short*) NewPtr(8192)))            { e = MemError(); abort(strGIFNoMem) }
  255.         if (!(Suffix = (short*) NewPtr(8192)))            { e = MemError(); abort(strGIFNoMem) }
  256.         if (!(BufferBase = (Byte*) NewPtr(BufferSize)))    { e = MemError(); abort(strGIFNoMem) }
  257.         
  258.         // get the file size
  259.         
  260.         e = GetEOF(refNum,&FileSize);
  261.         
  262.         Read (4, &sig);                                                    /* Read the GIF signature */
  263.         if (sig != 'GIF8') { e = paramErr; abort(strInvalidGIFSig) }    /* and make sure it's correct */
  264.         Skip(6);                                                        /* Skip screen dimensions */
  265.         NextByte(flags);                                                /* This flag byte is processed below */
  266.         Skip(2);                                                        /* Skip background color and aspect ratio */
  267.         
  268.         ReadColorMap                                                    /* Read the global color map */
  269.  
  270.         while (TRUE)
  271.         {
  272.             NextByte(b);                                                /* Read the first byte of the block */
  273.             
  274.             switch (b)
  275.             {
  276.                 case ImageSeparator:                                    /* Image separator, let's go read the image */
  277.                     goto ReadImage;
  278.                 case ExtensionIntroducer:                                /* Extension block */
  279.                     Skip(1);                                            /* Skip the extension label */
  280.                     NextByte(b);                                        /* Get the block size */
  281.                     Skip(b);                                            /* Skip the block */
  282.                     
  283.                     NextByte(b);                                        /* Read the subsequent data blocks */
  284.                     while (b != NullBlock)
  285.                     {                                                    /* until a trailer block is reached */
  286.                         Skip(b);                                        /* Skip this block */
  287.                         NextByte(b);                                    /* Get length of the next block */
  288.                     }
  289.                     
  290.                     break;
  291.                 default:                                                /* Unknown file format */
  292.                     e = paramErr;
  293.                     abort(strUnknownGIFBlock);
  294.             }
  295.         }
  296.  
  297.     ReadImage:
  298.         Skip(4);                                                        /* Skip left and top offsets */
  299.         NextByte(b); NextByte(c);                                        /* Read width and height */
  300.         Width = (short) c * 0x100 + b;                                    /* Byte per byte because they are written */
  301.         NextByte(b); NextByte(c);                                        /* in stupid PC-like big-endian order */
  302.         Height = (short) c * 0x100 + b;
  303.         
  304.         NextByte(flags);
  305.         Interlaced = (flags & InterlaceMask) ? true : false;            /* See if the image is interlaced */
  306.  
  307.         ReadColorMap                                                    /* If there is a local color map, read it */
  308.         
  309.         if (!HasColorMap) { e = paramErr; abort(strNoColorMap) }        /* Make sure we have at least one color map */
  310.         
  311.         GetGWorld(&oldWorld, &oldDevice);                                /* save the current world */
  312.         SetRect(&bounds, 0, 0, Width, Height);                            /* and create our own */
  313.         
  314.         (*ColorTable)->ctSeed = GetCTSeed();        // $$GPC- set some seed value rather than zero (in 7.5.3+, 
  315.                                                     // failure to do this will hang the system)
  316.         
  317.         gwflags = 0;
  318.         
  319.         if ( e = NewGWorld( world, 8, &bounds, ColorTable, NULL, gwflags ) )
  320.             abort( strGIFNoMem )
  321.  
  322.         LockPixels(GetGWorldPixMap(*world));                            /* Lock the pixel map */
  323.         SetGWorld (*world, NULL);                                        /* activate it */
  324.  
  325.         NextByte(CodeSize);
  326.         ClearCode = (1 << CodeSize);
  327.         EOFCode = ClearCode + 1;
  328.         FreeCode = FirstFree = ClearCode + 2;
  329.         
  330.         CodeSize++;
  331.         InitCodeSize = CodeSize;
  332.         MaxCode = (1 << CodeSize);
  333.         ReadMask = MaxCode - 1;
  334.  
  335.         BlockCount = 0;                                                    /* We start at the beginning of a block */
  336.         BitOffset = 0;                                                    /* and at the beginning of a byte */
  337.         BufferPointer = BufferTop = BufferBase + BufferSize;            /* Force the buffer to be filled immediately */
  338.         
  339.         xc = yc = 0;
  340.         Pass = 0;
  341.  
  342.         srcPixMap = GetGWorldPixMap(*world);                            /* Get the pixmap */
  343.         rowBytes = (*srcPixMap)->rowBytes & 0x7FFF;                        /* get rowBytes */
  344.         curAddr = srcBaseAddr = (long*) GetPixBaseAddr (srcPixMap);        /* and the base address of the pixmap */
  345.         
  346.         ReadCode(Code);
  347.         while (Code != EOFCode)
  348.         {                            
  349.             if (Code == ClearCode)
  350.             {                                                            /* Clear code sets everything back to its */
  351.                 CodeSize = InitCodeSize;                                /* initial value, then reads the subsequent code */
  352.                 MaxCode = (1 << CodeSize);                                /* as uncompressed data. */
  353.                 ReadMask = MaxCode - 1;
  354.                 FreeCode = FirstFree;
  355.                 ReadCode(Code);
  356.                 CurCode = OldCode = Code;
  357.                 FinChar = CurCode & BitMask;
  358.                 DrawPixel(FinChar);
  359.             }
  360.             else
  361.             {                                                            /* If not Clear code, then must be data. */
  362.                 CurCode = InCode = Code;                                /* Save same as CurCode and InCode */
  363.  
  364.                 OutCode = OutCodeBase;
  365.                 if (CurCode >= FreeCode)
  366.                 {                                                        /* If >= FreeCode, not in the hash table yet */
  367.                     CurCode = OldCode;                                    /* repeat the last character decoded */
  368.                     *(OutCode++) = FinChar;
  369.                 }
  370.                 
  371.                 while (CurCode > BitMask)
  372.                 {                                                        /* Pursue the chain pointed to by CurCode */
  373.                     *(OutCode++) = Suffix[CurCode];                        /* through the hash table to its end */
  374.                     CurCode = Prefix[CurCode];                            /* the output queue */
  375.                 }
  376.  
  377.                 FinChar = CurCode & BitMask;                            /* The last code in the chain is treated as raw data. */
  378.                 *OutCode = FinChar;
  379.                             
  380.                 while (OutCode >= OutCodeBase)                            /* Now we put the data out */
  381.                     DrawPixel(*(OutCode--));                            /* It's been stacked LIFO, so deal with it that way */
  382.  
  383.                 Prefix[FreeCode] = OldCode;                                /* Build the hash table on-the-fly */
  384.                 Suffix[FreeCode] = FinChar;                                /* No table is stored in the file */
  385.                 OldCode = InCode;
  386.                 
  387.                 if (++FreeCode >= MaxCode)                                /* Point to the next slot in the table */
  388.                     if (CodeSize < 12)
  389.                     {                                                    /* If we exceed the current MaxCode value */
  390.                         CodeSize++;                                        /* increment the code size unless it's already 12 */
  391.                         MaxCode *= 2;                                    /* If it is, do nothing; the next code better be */
  392.                         ReadMask = MaxCode - 1;                            /* Clear */
  393.                     }                                                
  394.             }
  395.  
  396.             ReadCode(Code);                                                /* Read the next code */
  397.         }
  398.         
  399.         UnlockPixels(GetGWorldPixMap(*world));                            /* Unlock the pixel map */
  400.         SetGWorld (oldWorld, oldDevice);                                /* restore the previous graphics world */
  401.         
  402.         e = noErr;
  403.     }
  404.     catch( OSErr err )
  405.     {
  406.         if (ColorTable)                                                    /* We can destroy the CTable, NewGWorld */
  407.             DisposeHandle((Handle) ColorTable);                        
  408.                 
  409.         if (BufferBase)
  410.             DisposePtr((Ptr) BufferBase);
  411.             
  412.         if (Suffix)
  413.             DisposePtr((Ptr) Suffix);
  414.             
  415.         if (Prefix)
  416.             DisposePtr((Ptr) Prefix);
  417.             
  418.         if (OutCodeBase)
  419.             DisposePtr((Ptr) OutCodeBase);
  420.             
  421.         throw err;
  422.     }
  423. end:
  424.     if (ColorTable)                                                    /* We can destroy the CTable, NewGWorld */
  425.         DisposeHandle((Handle) ColorTable);                        
  426.             
  427.     if (BufferBase)
  428.         DisposePtr((Ptr) BufferBase);
  429.         
  430.     if (Suffix)
  431.         DisposePtr((Ptr) Suffix);
  432.         
  433.     if (Prefix)
  434.         DisposePtr((Ptr) Prefix);
  435.         
  436.     if (OutCodeBase)
  437.         DisposePtr((Ptr) OutCodeBase);
  438.         
  439.     return(e);
  440. }